﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SwanEngine.Astar
{
    /// <summary>
    /// A*格子生成类
    /// by lijia
    /// </summary>
    public class MapGridBuilder : MonoBehaviour
    {
        /// <summary>
        /// 格子的缩放
        /// </summary>
        public const float GridSizeScale = 1f;

        /// <summary>
        /// 障碍物层
        /// </summary>
        public const string LAYER_OBSTACLE = "obstacle";
        /// <summary>
        /// 可行走区域
        /// </summary>
        public const string TAG_WALKABLE = "walkable";

        public Dictionary<string, GridNodeVO> nodeDic = new Dictionary<string, GridNodeVO>();

        public MapDataVo mapVO = new MapDataVo();

        public string sceneId = "";


        private GameObject _itemList;
        private GameObject itemList
        {
            get
            {
                if (_itemList == null)
                {
                    _itemList = new GameObject("itemList");
                    _itemList.transform.SetParent(this.transform);
                }
                return _itemList;
            }
        }

        private Shader _gridShader;
        private Shader gridShader
        {
            get
            {
                if (_gridShader == null)
                {
                    _gridShader = Shader.Find("swan/Grid");
                }
                return _gridShader;
            }
        }

        public string GetAStarFilePath()
        {
            return Application.dataPath + "/Resources/Scene/" + this.sceneId + "/Astar.txt";
        }

        /// <summary>
        /// 烘培地形
        /// </summary>
        public void BakeGrid()
        {
            this.nodeDic.Clear();

            int minR = int.MaxValue, maxR = 0, minC = int.MaxValue, maxC = 0;

            GameObject[] walkablesList = GameObject.FindGameObjectsWithTag(MapGridBuilder.TAG_WALKABLE);
            foreach (GameObject walkables in walkablesList)
            {
                BoxCollider collider = walkables.GetComponent<BoxCollider>();
                Vector2[] Corners2D = GetColliderCorners2D(walkables);
                // Corners2D[0] += new Vector2(0.5f, 0.5f);
                // Corners2D[1] += new Vector2(0.5f, 0.5f);

                float panelY = collider.transform.position.y + collider.center.y + (collider.size.y / 2);

                for (int i = (int)Corners2D[0].x; i < Corners2D[1].x; i++)
                {
                    for (int j = (int)Corners2D[0].y; j < Corners2D[1].y; j++)
                    {
                        //计算出最小行列
                        if (i < minC)
                        {
                            minC = i;
                        }
                        else if (i > maxC)
                        {
                            maxC = i;
                        }

                        if (j < minR)
                        {
                            minR = j;
                        }
                        else if (j > maxR)
                        {
                            maxR = j;
                        }

                        GridNodeVO nodeVO = null;
                        string key = ToKey(i, j);
                        this.nodeDic.TryGetValue(key, out nodeVO);
                        if (nodeVO != null) continue;//防止重复格子

                        nodeVO = new GridNodeVO(i, j);
                        nodeVO.y = panelY;
                        Vector3 nodePos = new Vector3(i, panelY, j);

                        //-------------------------检测是否可走Start----------------------
                        LayerMask layerMask = 1 << LayerMask.NameToLayer(MapGridBuilder.LAYER_OBSTACLE);
                        int raydistance = 20;
                        Vector3 rayDir = Vector3.down;

                        Vector3 rayStart = nodePos * MapGridBuilder.GridSizeScale + new Vector3(0.5f, 0, 0.5f);

                        Vector3[] corner = new Vector3[5];
                        corner[0] = Vector3.forward + Vector3.left;
                        corner[1] = Vector3.back + Vector3.left;
                        corner[2] = Vector3.forward + Vector3.right;
                        corner[3] = Vector3.back + Vector3.right;
                        corner[4] = Vector3.zero;

                        RaycastHit hitInfo;
                        for (int k = 0; k < corner.Length; k++)
                        {
                            Vector3 offsetPos = corner[k] * 0.4f * MapGridBuilder.GridSizeScale;//格子转世界
                            rayStart += offsetPos;
                            rayStart.y = 10;
                            if (Physics.Raycast(rayStart, rayDir, out hitInfo, raydistance, layerMask))
                            {
                                nodeVO.isWalkable = false;
                                break;
                            }
                        }
                        //-------------------------检测是否可走End----------------------
                        this.nodeDic.Add(key, nodeVO);
                    }
                }
            }

            if (this.nodeDic.Count > 0)
            {
                this.mapVO = new MapDataVo();
                this.mapVO.gridScale = MapGridBuilder.GridSizeScale;
                this.mapVO.offsetX = minC;
                this.mapVO.offsetZ = minR;

                int rowsNum = maxR - minR + 1;
                int colsNum = maxC - minC + 1;

                this.mapVO.tileRow = rowsNum;
                this.mapVO.tileCol = colsNum;

                byte[,] title = new byte[colsNum, rowsNum];
                foreach (GridNodeVO nodeVO in this.nodeDic.Values)
                {
                    int rows = nodeVO.z - mapVO.offsetZ;
                    int cols = nodeVO.x - mapVO.offsetX;
                    try
                    {
                        title[cols, rows] = nodeVO.GetStatusBit();
                    }
                    catch (System.Exception)
                    {
                        Debug.LogError("数组越界 z:" + nodeVO.z + "  x:" + nodeVO.x);
                    }
                }
                mapVO.tiles = title;

                string path = GetAStarFilePath();
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
                formatter.Serialize(stream, mapVO);
                stream.Close();

                Debug.Log("烘焙成功：" + path);
            }
        }

        /// <summary>
        /// 显示格子
        /// </summary>
        public void ShowGrid()
        {
            if (this.nodeDic.Count == 0)
            {
                return;
            }
            HideGrid();
            foreach (GridNodeVO nodeVO in this.nodeDic.Values)
            {
                Vector3 nodePos = new Vector3(nodeVO.x, nodeVO.y, nodeVO.z);
                Material material = new Material(gridShader);
                if (!nodeVO.isWalkable)
                {
                    material.color = Color.red;
                }

                GameObject nodeCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                nodeCube.transform.SetParent(itemList.transform);
                nodeCube.transform.localScale = Vector3.one * MapGridBuilder.GridSizeScale * 0.7f;
                nodeCube.transform.position = nodePos * MapGridBuilder.GridSizeScale + new Vector3(0.5f, 0, 0.5f);//格子转世界
                nodeCube.GetComponent<MeshRenderer>().material = material;
                nodeCube.GetComponent<BoxCollider>().enabled = false;
                nodeVO.go = nodeCube;
                nodeCube.name = ToKey(nodeVO.x, nodeVO.z);
            }
        }

        /// <summary>
        /// 隐藏格子
        /// </summary>
        public void HideGrid()
        {
            GameObject.DestroyImmediate(this.itemList);
            this._itemList = null;
        }

        public GameObject startGO;
        public GameObject endGO;


        public void InitAstart()
        {
            FindPathProxy.Instance.SetMapData(this.mapVO);
            FindPathProxy.Instance.astar.nodeDic = nodeDic;
            Debug.Log("InitAstart");
        }

        public void FindPath()
        {
            FindPathProxy.Instance.FindPathByWorldPos(startGO.transform.position, endGO.transform.position);
        }

        /// <summary>
        /// 从本地读取格子文件构建节点
        /// </summary>
        public bool LoadAstarFile()
        {
            string path = GetAStarFilePath();
            do
            {
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None);
                this.mapVO = formatter.Deserialize(stream) as MapDataVo;
                stream.Close();
                if (this.mapVO == null)
                {
                    break;
                }
                nodeDic.Clear();
                for (int i = 0; i < mapVO.tileCol; i++)
                {
                    for (int j = 0; j < mapVO.tileRow; j++)
                    {
                        GridNodeVO node = new GridNodeVO(i, j);
                        node.x += mapVO.offsetX;
                        node.z += mapVO.offsetZ;
                        string key = ToKey(node.x, node.z);
                        node.SetStatusBit(mapVO.tiles[i, j]);
                        nodeDic.Add(key, node);
                    }
                }
                return true;

            } while (false);

            return false;
        }

        /// <summary>
        /// 计算一下包围盒角落的坐标,这里只需要平面坐标
        /// </summary>
        private static Vector2[] GetColliderCorners2D(GameObject panel)
        {
            Vector2[] corners = new Vector2[2];

            BoxCollider collider = panel.GetComponent<BoxCollider>();

            //这里取世界坐标的点好了
            Vector4 panelOffset = collider.transform.position;

            float halfWidth = collider.size.x / 2;
            float halfLength = collider.size.z / 2;

            //计算包围盒边界点
            Vector3 vec1 = new Vector3();
            vec1.x = (collider.center.x - halfWidth);
            vec1.z = (collider.center.z - halfLength);

            Vector3 vec2 = new Vector3();
            vec2.x = (halfWidth + collider.center.x);
            vec2.z = (halfLength + collider.center.z);

            //转世界坐标+偏移
            Vector4 vecTemp = (collider.transform.localToWorldMatrix * vec1) + panelOffset;
            vecTemp /= MapGridBuilder.GridSizeScale;//世界转格子

            corners[0] = new Vector2(vecTemp.x, vecTemp.z);

            vecTemp = collider.transform.localToWorldMatrix * vec2 + panelOffset;
            vecTemp /= MapGridBuilder.GridSizeScale;//世界转格子

            corners[1] = new Vector2(vecTemp.x, vecTemp.z);

            return corners;
        }

        private static string ToKey(int x, int z)
        {
            return x + "-" + z;
        }

    }
}